﻿#include "IDataStore.h"
#include <stdlib.h>
#include "SelfDB.h"
#include <string.h>
#include <assert.h>
#include <boost/make_shared.hpp>

#include "Sqlite3Store.h"
//#include "../FacilityBaseLib/DateTime.h"
#include "../FacilityBaseLib/DateTimeHelper.h"
#include "../FacilityBaseLib/Container.h"

boost::shared_ptr<IDataStore> IDataStore::CreateStore( const char * rootpath, int nDBType /*= dbtypeUnknown */ )
{
	boost::shared_ptr<IDataStore> pRet;

	// argument test
 	if(!rootpath || strlen(rootpath)<=0 || strlen(rootpath)>1023 )
 		return NULL;

	// test rootpath type one by one
	char accurateroot[1024];
	memset( accurateroot, 0, sizeof(accurateroot) );
	switch( nDBType )
	{
	case dbtypeUnknown:
	{
		//if (CSelfDB::GetAccurateRoot(rootpath, accurateroot, 1024))
		//	pRet = new CSelfDB(accurateroot, true);
		//else
		//	return NULL;
		return nullptr;
	}
		break;
	case dbtypeSelfDB:
		{
//			string file_full_str(rootpath);
// 			string::size_type save_path_idx=file_full_str.find("file://");
// 			if (string::npos==save_path_idx)
// 			{
// 				return NULL;
// 			}
// 			else
// 			{
// 				save_path_idx+=string("file://").length();
// 			}
//			string file_save_path=file_full_str/*.substr(save_path_idx)*/;
//			if( CSelfDB::GetAccurateRoot( file_save_path.c_str(), accurateroot, 1024 ) )
//				pRet	=	new CSelfDB( accurateroot, true );
			return nullptr;
		}
		break;
	/*case dbtypeQianlong:
		if( CQianlong::GetAccurateRoot( rootpath, accurateroot, 1024 ) )
			pRet	=	new CQianlong( accurateroot, true );
		break;
		*/
	case dbtypeSqlite3:
		{
			Sqlite3Store* dao= new Sqlite3Store();
			if (dao->Init(rootpath)!=0)
			{
				delete dao;
				dao=nullptr;
			}
			pRet.reset(dao);
		}
		break;
	case dbtypeODBC:
		{
//#ifdef _WIN32
//			DatabaseOperator* dao=new DatabaseOperator();
//#else
//            Sqlite3Operator* dao= new Sqlite3Operator();
//#endif
//			if (dao->Init(rootpath)!=0)
//			{
//				delete dao;
//				dao=NULL;
//			}
//			pRet=dao;
			return nullptr;
		}
		

		break;
	default:
		return nullptr;
	}

	return pRet;
}

int IDataStore::GetSupportedDataType(vector<string>&  pdbtype)
{
// 	pdbtype.push_back(DatabaseOperator::GetDBTypeName());
// 	pdbtype.push_back(CSelfDB::GetDBTypeName());
	return pdbtype.size();
}

IDataStore::~IDataStore()
{
	memset(m_szRootPath,0,sizeof(m_szRootPath));
	m_bIsOK=false;
}

IDataStore::IDataStore()
{
	memset(m_szRootPath,0,sizeof(m_szRootPath));
	m_bIsOK=false;
}


boost::posix_time::ptime IDataStore::GetTimeInitial()
{
	return boost::posix_time::ptime(boost::gregorian::date(1990, 12, 19),boost::posix_time::time_duration(0, 0, 0));
}

bool IDataStore::GetTimeLocalRange(boost::posix_time::ptime *ptmLatest, boost::posix_time::ptime * ptmPioneer, boost::posix_time::ptime * ptmInitial)
{
	InstrumentDataSPtr	stock = boost::make_shared<InstrumentData>();
	stock->SetInstrumentID( (char*)InstrumentData::marketSHSE, "IF" );
	
	if( PrepareData(stock, InstrumentData::dataK, ktypeDay ) )
		return false;
	KdataContainer	& kday	=	stock->GetKDataDay();

	int	nYear, nMonth, nDay, nHour, nMinute;
	if( ptmLatest )
	{
		if( !kday.LatestDate( nYear, nMonth, nDay, nHour, nMinute ) )
			return false;
		*ptmLatest	= boost::posix_time::ptime(boost::gregorian::date(nYear, nMonth, nDay), boost::posix_time::time_duration(23, 59, 59));
	}

	if( ptmPioneer )
	{
		if( kday.size() == 0 || !kday.DateAt(0, nYear, nMonth, nDay, nHour, nMinute ) )
			return false;
		*ptmPioneer	= boost::posix_time::ptime(boost::gregorian::date(nYear, nMonth, nDay), boost::posix_time::time_duration(0, 0, 0));
	}

	if( ptmInitial )
	{
		*ptmInitial	=	GetTimeInitial();
	}

	return true;
}

bool IDataStore::GetNeedDownloadRange(InstrumentInfo &info, boost::posix_time::ptime tmBegin, 
	boost::posix_time::ptime tmEnd, boost::posix_time::ptime &tmDLBegin, boost::posix_time::ptime &tmDLEnd)
{
	tmDLBegin	=	tmBegin;
	tmDLEnd		=	tmEnd;

	InstrumentDataSPtr	stock = boost::make_shared<InstrumentData>();
	// Prepare Data
	stock->SetInstrumentInfo( &info );
	if( !PrepareData(stock, InstrumentData::dataK, ktypeDay ) )
		return true;
	KdataContainer	& kday	=	stock->GetKDataDay();

	// Prepare Time
	boost::posix_time::ptime	tmInitial, tmLatest, tmPioneer;
	boost::posix_time::ptime	sptimeInitial;
	if (0 != info.m_datebegin && FromInstrumentTimeDayOrMin(sptimeInitial, info.m_datebegin,true))
		tmInitial	=	sptimeInitial;
	else
		tmInitial	=	GetTimeInitial( );

	int	nYear, nMonth, nDay, nHour, nMinute;
	if( !kday.LatestDate( nYear, nMonth, nDay, nHour, nMinute ) )
		return true;
	tmLatest = boost::posix_time::ptime(boost::gregorian::date(nYear, nMonth, nDay), boost::posix_time::time_duration(23, 59, 59));

	if( kday.size() == 0 || !kday.DateAt(0, nYear, nMonth, nDay, nHour, nMinute ) )
		return true;
	tmPioneer = boost::posix_time::ptime(boost::gregorian::date(nYear, nMonth, nDay), boost::posix_time::time_duration(0, 0, 0));

	// Deal With
	if( tmBegin < tmInitial )
		tmBegin	=	tmInitial;

	if( tmBegin >= tmPioneer && tmEnd <= tmLatest )
	{
		int	nCount	=	0;
		for( size_t k=0; k<kday.size(); k++ )
		{
			DWORD	date	=	kday.at(k).TradingDate;
			boost::posix_time::ptime	sptime;
			if (FromInstrumentTimeDayOrMin(sptime,date,true))
			{
				if( sptime >= tmBegin && sptime <= tmEnd )
					nCount	++;
			}
		}

		auto	span	=	tmEnd.date() - tmBegin.date();
		if( nCount > span.days()*4/7 )
            return false;
	}

	if( tmEnd <= tmPioneer )
	{
		tmDLBegin	=	tmBegin;
		tmDLEnd	=	tmPioneer-boost::gregorian::days(1);
	}
	else if( tmBegin >= tmLatest )
	{
		tmDLBegin = tmLatest + boost::gregorian::days(1);
		tmDLEnd	=	tmEnd;
	}
	else if( tmBegin < tmPioneer && tmEnd > tmPioneer && tmEnd <= tmLatest )
	{
		tmDLBegin	=	tmBegin;
		tmDLEnd = tmPioneer - boost::gregorian::days(1);
	}
	else if( tmBegin >= tmPioneer && tmBegin < tmLatest && tmEnd > tmLatest )
	{
		tmDLBegin = tmLatest + boost::gregorian::days(1);
		tmDLEnd	=	tmEnd;
	}
	else
	{
		tmDLBegin	=	tmBegin;
		tmDLEnd		=	tmEnd;
	}
	return true;
}

int IDataStore::StoreExchange(ExchangeContainer& exchanges)
{
	return exchanges.size();
}

int IDataStore::LoadExchange(ExchangeContainer& exchanges)
{
	return exchanges.size();
}

int IDataStore::PrepareData(InstrumentDataSPtr pInstrument, int enKtype, int period /*= ktypeDay*/, int nLength /*= DEFAULT_LOAD_MIN1_COUNT*/, bool bReload /*= false*/)
{
	auto m_pDataStoreOperator = this;

	switch (enKtype)
	{
	case InstrumentData::dataInfo:
	{
		auto& info = pInstrument->GetInstrumentInfo();
		return InstrumentContainer::get_instance().GetInstrumentInfo(
			info.get_id(), &info);
	}
	case InstrumentData::dataK:
	{
		switch (period)
		{
		case ktypeMonth:
			if (bReload || pInstrument->GetKDataMonth().size() == 0)
				m_pDataStoreOperator->LoadKData(pInstrument.get(), ktypeMonth, nLength);
			break;
		case ktypeWeek:
			if (bReload || pInstrument->GetKDataWeek().size() == 0)
				m_pDataStoreOperator->LoadKData(pInstrument.get(), ktypeWeek, nLength);
			break;
		case ktypeDay:
			if (bReload || pInstrument->GetKDataDay().size() == 0)
				m_pDataStoreOperator->LoadKData(pInstrument.get(), ktypeDay, nLength);
			break;		// month, week, and day willnot extract from min60,min30...

		case ktypeMin60:
			if (bReload || pInstrument->GetKDataMin60().size() == 0)
				m_pDataStoreOperator->LoadKData(pInstrument.get(), ktypeMin60, nLength);
			break;
		case ktypeMin30:
			if (bReload || pInstrument->GetKDataMin30().size() == 0)
				m_pDataStoreOperator->LoadKData(pInstrument.get(), ktypeMin30, nLength);
			break;
		case ktypeMin15:
			if (bReload || pInstrument->GetKDataMin15().size() == 0)
				m_pDataStoreOperator->LoadKData(pInstrument.get(), ktypeMin15, nLength);
			break;
		case ktypeMin5:
			if (bReload || pInstrument->GetKDataMin5().size() == 0)
				m_pDataStoreOperator->LoadKData(pInstrument.get(), ktypeMin5, nLength);
			break;
		case ktypeMin:
			if (bReload || pInstrument->GetKDataMin1().size() == 0)
				return m_pDataStoreOperator->LoadKData(pInstrument.get(), ktypeMin, nLength);
			else
				return pInstrument->GetMinute().size();
		default:
			assert(false);
			return 0;
		}
		return pInstrument->ExtractKData(period, false);
	}
	// 	case dataDR:
	// 		if( bReload || GetDRData().GetSize() == 0 )
	// 			return m_pDataStoreOperator->LoadDRData( this );
	// 		else
	// 			return GetDRData().GetSize();
	case InstrumentData::dataBasetable:
	{
		auto& info = pInstrument->GetInstrumentInfo();
		return InstrumentContainer::get_instance().GetInstrumentInfo(
			info.get_id(), &info);
	}

	case InstrumentData::dataBasetext:
		if (bReload || !pInstrument->GetBaseTextPtr())
			return m_pDataStoreOperator->LoadBaseText(pInstrument.get());
		else
			return pInstrument->GetBaseTextLength();
	case InstrumentData::dataReport:
		if (bReload || pInstrument->GetReport().size() == 0)
			return m_pDataStoreOperator->LoadReport(pInstrument.get(), nLength);
		else
			return pInstrument->GetReport().size();
	case InstrumentData::dataMinute:
		if (bReload || pInstrument->GetMinute().size() == 0)
			return m_pDataStoreOperator->LoadMinute(pInstrument.get(), nLength);
		else
			return pInstrument->GetMinute().size();
		// 	case dataOutline:
		// 		if( bReload || GetOutline().GetSize() == 0 )
		// 			return m_pDataStoreOperator->LoadOutline( this );
		// 		else
		// 			return GetOutline().GetSize();
	default:
		assert(false);
		return 0;
	}
}
